﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using TransferPost.Beans;
using TransferPost.util;
using System.Linq;
using System.Text;

namespace TransferPost.checkpoints
{
    /// <summary>
    /// 
    /// </summary>
    public class QACheckPoint : BaseCheckPoint
    {
        /*
       * https://fdoc.epoint.com.cn/onlinedoc/rest/d/jayemq
       * 个性化代码检测的重要要素
       * 1、文件判断，通过文件名 和 路径上要有工程名字来约束
       * 
       * 2、先判断顶部是不是有声明，如果顶部有则不需要关注，
       *    顶部的判断依据是 public class 的上面。
       * 
       * 3、如果顶部没有声明，则在整个代码内部，按照注释和逻辑关系，进行常规的几个检测点检测整改修改情况，
       *    可以预先埋设多个点
       */

        // 整改编号
        public string qaCode;

        // 需要填写的编号
        public List<QACheckPointFileProperty> property = new List<QACheckPointFileProperty>();

        // 找到的文件路径和关联的检查点特征。这个是需要进行干活和检查的
        public Dictionary<string, QACheckPointFileProperty> files = new Dictionary<string, QACheckPointFileProperty>();

        // 找到，存在文件的pro 清单
        Dictionary<string, SVNProjectInfo> findpros = new Dictionary<string, SVNProjectInfo>();

        // 结果
        public Dictionary<SVNProjectInfo, List<QASignleFileSpyInfo>> AnalysisRtn = new Dictionary<SVNProjectInfo, List<QASignleFileSpyInfo>>();

        /// <summary>
        /// 寻找文件
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool PrepareCheck(object obj)
        {
            // 参数名称
            Dictionary<string, object> args = (Dictionary<string, object>)obj;

            string strRootPath = args["rootpath"].ToString();

            foreach (QACheckPointFileProperty item in property)
            {
                // 调用everything的sdk

                string strSearchKey = string.Format("-w {0} -s -p {1}", item.fileName, strRootPath);

                // es -w pro.json -s -p d:\ywcode

                Process process = new Process();
                process.StartInfo.FileName = @"es";
                process.StartInfo.Arguments = strSearchKey;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.CreateNoWindow = true;
                //process.StartInfo.StandardOutputEncoding = System.Text.Encoding.UTF8;
                process.StartInfo.StandardErrorEncoding = System.Text.Encoding.UTF8;
                process.Start();

                // 逐行输出
                process.OutputDataReceived += (s, e1) =>
                {
                    if (e1.Data != null)
                    {
                        bool blnIsOnThePathCheck = true;

                        string strSinglePath = e1.Data;

                        foreach (string pathArg in item.FilePathContainsArg)
                        {
                            if (blnIsOnThePathCheck && strSinglePath.IndexOf(pathArg, StringComparison.OrdinalIgnoreCase) < 0)
                            {
                                // 有一个路径上的特征字符不符合，就直接跳出
                                // 一般我们用工程名称做特征字符
                                blnIsOnThePathCheck = false;
                            }
                        }

                        // 所有的都过了
                        if (blnIsOnThePathCheck == true)
                        {
                            // 说明满足条件
                            files.Add(strSinglePath, item);
                        }
                    }
                };

                process.BeginOutputReadLine();

                string error = process.StandardError.ReadToEnd();

                if (!string.IsNullOrWhiteSpace(error))
                {
                    //throw new Exception(string.Format("检索命令出现异常：{0}", strSearchKey));
                    MyLogger.Error(string.Format("{0}，检索命令出现异常：{1}", this.qaCode, strSearchKey));

                    return false;
                }

                process.WaitForExit();
            }

            foreach (var item in files)
            {
                string strSignleProPath = item.Key.Substring(0, item.Key.IndexOf('\\', strRootPath.Length));
                string strSignleProCfg = Path.Combine(strSignleProPath, "pro.json");

                SVNProjectInfo procfg = Newtonsoft.Json.JsonConvert.DeserializeObject<SVNProjectInfo>(File.ReadAllText(strSignleProCfg));

                // 文件名称和工程名称
                findpros.Add(item.Key, procfg);
            }

            return true;
        }

        /// <summary>
        /// 执行检查
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override RtnBean DoCheck(object obj)
        {
            AnalysisRtn.Clear();

            //有点冗余，暂时先这样吧
            Dictionary<string, object> args = (Dictionary<string, object>)obj;

            string strRootPath = args["rootpath"].ToString();

            List<SVNProjectInfo> findpros = args["findpros"] as List<SVNProjectInfo>;

            // 遍历
            foreach (KeyValuePair<string, QACheckPointFileProperty> item in files)
            {
                // 读取里头的行数
                // 不添加到实体类中，防止内存过大
                string[] contents = File.ReadAllLines(item.Key);

                QACheckPointFileProperty checkPoints = item.Value;

                QASignleFileSpyInfo info = SpyFile(contents);

                // 无需检查
                if (info.NoCheckRequired == true)
                {
                    info.CheckIsOk = true;
                    PushRtn(item.Key, info);
                }
                else
                {
                    // 需要检查，就根据特征点，挨个过，如果有一个特征点不过，那么这个文件就不过。

                    // 根据特征点一个个检查

                    foreach (var i in info.lc_qacodelines)
                    {
                        int j = i;

                        string strNext3Line = "";

                        // 多拿3行
                        for (int i_MaxNearLineCount = 1; i_MaxNearLineCount < 4; i_MaxNearLineCount++)
                        {
                            if (i + i_MaxNearLineCount < contents.Length && !contents[i + i_MaxNearLineCount].TrimStart().StartsWith("//"))
                            {
                                strNext3Line += (contents[i + i_MaxNearLineCount]);
                            }
                        }

                        // 找到注释下的第一行，然后一整行做特征判断 

                        for (int s = 0; s < checkPoints.AppearWords.Count; s++)
                        {
                            bool ThisLineAllWordIsOk = true;
                            foreach (var word in checkPoints.AppearWords[s])
                            {
                                if (!strNext3Line.Contains(word))
                                {
                                    ThisLineAllWordIsOk = false;
                                    break;
                                }
                            }

                            if (ThisLineAllWordIsOk == true)
                            {
                                // 检测通过， 就移除掉这个特征点。
                                checkPoints.AppearWords.RemoveAt(s);
                            }
                        }
                    }

                    // 把所有的 修改点送过去之后，发现还剩检测点没通过的话，这个时候就要来事情了
                    if (checkPoints.AppearWords.Count == 0)
                    {
                        //说明检测通过
                        info.CheckIsOk = true;
                    }
                    else
                    {
                        // 这个都是没搞定的
                        info.ApperWordCheckResult.AddRange(checkPoints.AppearWords);
                        info.CheckIsOk = false;
                    }

                    PushRtn(item.Key, info);
                }
            }

            return base.DoCheck(obj);
        }

        /// <summary>
        /// 统一追加
        /// </summary>
        /// <param name="strFilePath"></param>
        /// <param name="info"></param>
        private void PushRtn(string strFilePath, QASignleFileSpyInfo info)
        {
            // 这个肯定是存在的
            SVNProjectInfo pro = findpros[strFilePath];

            List<QASignleFileSpyInfo> v = null;

            AnalysisRtn.TryGetValue(pro, out v);

            if (v == null)
            {
                AnalysisRtn.Add(pro, new List<QASignleFileSpyInfo>() { info });
            }
            else
            {
                v.Add(info);
            }
        }

        private QASignleFileSpyInfo SpyFile(string[] contents)
        {
            QASignleFileSpyInfo info = new QASignleFileSpyInfo();

            int i_lineCount = 0;
            foreach (string line in contents)
            {
                i_lineCount++;

                #region " QA-T-AQ-1912-442 出现的地方，注意是不是有无需整改 "
                int i_qacode = line.IndexOf(qaCode);

                if (i_qacode > 0)
                {
                    if (line.IndexOf("无需整改") > 0)
                    {
                        info.NoCheckRequired = true;

                        return info;
                    }
                    else
                    {
                        // 这行记录下来。
                        info.lc_qacodelines.Add(i_lineCount);
                    }
                }

                #endregion

                #region " 判断class头的位置 "
                int i_class = line.IndexOf(" class ");

                // 说明是找到了，前面没注释的
                if (i_class > 0 && (line.IndexOf('/', 0, i_class) < 0))
                {
                    info.lc_classhead = i_lineCount;
                }
                #endregion
            }

            return info;
        }
    }

    /// <summary>
    /// 先分析一下文件
    /// </summary>
    public class QASignleFileSpyInfo
    {
        /// <summary>
        /// 文件存储路径
        /// </summary>
        public string filePath = "";

        public bool NoCheckRequired = false;

        // java cs js html html
        public string extends;

        /// <summary>
        ///  不管是.NET还是java，class头的行数
        ///  --------
        ///  这里暂时不考虑一个文件里头有多个class类的龌龊情况
        /// </summary>
        public int lc_classhead = 0;

        /// <summary>
        /// 注释里头出现qacode的地方，这里注意，是在lc_classhead之后的地方的；
        /// 可能是多个，
        /// </summary>
        public List<int> lc_qacodelines = new List<int>();

        /// <summary>
        /// 这个是这个文件，还有哪些特征点的行， 没检测到位的
        /// </summary>
        public List<List<string>> ApperWordCheckResult = new List<List<string>>();

        /// <summary>
        /// 为了便于精简代码，ApperWordCheckResult为空则为true
        /// </summary>
        public bool CheckIsOk = false;
    }

    /// <summary>
    /// 一个整改点，可能对应多个文件
    /// 一个文件，可能对应多行
    /// </summary>
    public class QACheckPointFileProperty
    {
        //整改编号
        //public string qaCode;

        // 人为编号
        public int propertyid;

        // 要检测的文件名称
        public string fileName;

        // 文件所在路径上的特征符号，如果为空，则不需要判断.
        // 一般指工程名称
        public List<string> FilePathContainsArg;

        // 在这个文件中，如果有多个修改，那么在多处修改上的，每一行特征点，特征点可以多个
        public List<List<string>> AppearWords;
    }
}
